#!/usr/bin/env python
# encoding: utf-8
"""
@file: sanjiaoxing_zuizhi.py
@time: 2017/4/25 下午3:38
"""
# 三角形的最值问题
# 三角形的最值问题 会首先检测内存中是否有三角形对象，有的话 在进行求最值, 否则不进行
# 一般遇到三角形条件的话，内存中都会存放一个三角形key TRIANGLE_NAME_SEARCH_KEY
from mathsolver.functions.base import *
from sympy import sin, cos, tan, pi, symbols, sqrt
from mathsolver.functions.sanjiao import sanjiao_utils as su
from mathsolver.functions.budengshi import common_opers as co
from mathsolver.functions.sanjiao import sanjiaoxing_utils as sjx_utils
from mathsolver.functions.sanjiao.zuizhi import SanJiaoHanShuZuiZhi


# 给定三角关系和条件转换为三角函数表达式
# style1 Input  paramer1:三角形边角关系表达式(BaseEq or BasePoly) Ouput: 转换后的只含有一个函数的三角函数表达式, 并且输出其变量的取值范围
# 例1 \\triangle ABC中,C = \\frac{2π}{3},AB = 3,则\\triangle ABC的周长为( )
class Triangle2TrigF(BaseFunction):
    def solver(self, *args):
        self.label.add('解三角形')
        self.label.add('三角函数')
        known_eqs = sjx_utils.known_triangle_eqs(self.known)  # 查询known中可以得出的变量
        if known_eqs:
            known_eqs = map(lambda _: map(sjx_utils.preprocess_base_triangle_expr, _), known_eqs.sympify())
        else:
            known_eqs = []
        if isinstance(args[0], BasePoly):
            f_symbol = sympify('y')
            f_expr = args[0].sympify()
        elif isinstance(args[0], BaseSinEq) or isinstance(args[0], BaseEq):
            f_symbol, f_expr = args[0].sympify()
        else:
            raise Exception('Type Match Error')
        # 首先看能不能求出2*rside_values
        _symbs = list(set(map(lambda _: str(_).lower(), co.eqs_symbs(known_eqs))))
        _2r = None
        try:
            _symbs_values = co.isolve_eqs(BaseEqs(known_eqs), _symbs)
            for side_values in _symbs_values:
                for side_symb, side_value in side_values:
                    tmp_v = side_value / sin(str(side_symb).upper())
                    if tmp_v and tmp_v.is_real:
                        _2r = tmp_v
        except Exception:
            pass
        src_f_expr = f_expr
        # 首先针对 出现 A+B, B+C, A+C 同时出现的情况，可以先替换掉
        first_subs = (('A+B', 'pi - C'), ('A+C', 'pi - B'), ('B+C', 'pi - A'))
        f1 = f_expr.subs(first_subs)  # 先做这一步替换
        _a, _b, _c, _r = symbols('a, b, c, r')
        _A, _B, _C = symbols('A, B, C')
        side_values = {}  # 边的值
        angle_values = {}  # 角的值
        trig_values = {}  # 三角函数的值
        for _eq in known_eqs:
            l, r = _eq
            f = l - r
            symbs = list(f.free_symbols)
            if len(symbs) == 1:  # 如果只有一个变量的
                if su.i_is_subset(symbs, {_a, _b, _c}):  # 如果是边的变量
                    side = symbs[0]
                    side_value = co.isolve_eq2(BaseEq([f, 0]))[0]
                    side_values[side] = side_value
                elif su.i_is_subset(symbs, {_A, _B, _C}):  # 如果是角的变量
                    angle = symbs[0]
                    if str(f).find('sin') >= 0:  # 如果有sin的值
                        tmp_f = f.subs(sin(angle), 't')
                        sin_value = co.isolve_eq2(BaseEq([tmp_f, 0]))[0]
                        trig_values[sin(angle)] = sin_value
                    elif str(f).find('cos') >= 0:
                        tmp_f = f.subs(cos(angle), 't')
                        cos_value = co.isolve_eq2(BaseEq([tmp_f, 0]))[0]
                        trig_values[cos(angle)] = cos_value
                        trig_values[sin(angle)] = sqrt(1 - cos_value ** 2)
                    elif str(f).find('tan') >= 0:
                        tmp_f = f.subs(tan(angle), 't')
                        tan_value = co.isolve_eq2(BaseEq([tmp_f, 0]))[0]
                        trig_values[tan(angle)] = tan_value
                    else:  # 只含有角的值
                        angle_value = co.isolve_eq2(BaseEq([f, 0]))[0]
                        angle_values[angle] = angle_value
        f1 = f1.subs(side_values.items()).subs(trig_values.items()).subs(angle_values.items())
        self.steps.append(['由题意可知:', ''])
        self.steps.append([BaseEq([f_symbol, f_expr]).printing(), ''])
        if f1 != src_f_expr:
            self.steps.append([' =', new_latex(f1)])
        rest_sides = set(f1.free_symbols).intersection({_a, _b, _c})  # 剩下的边
        # --------------------------处理边--------------------
        if _2r:
            for s, sv in side_values.items():
                s_a = sympify(str(s).upper())  # 边对应的角
                if angle_values.get(s_a, None):
                    _2r = sv / sin(angle_values[s_a])
                    break
                elif trig_values.get(sin(s_a), None):
                    _2r = sv / trig_values[sin(s_a)]
                    break
        f2 = f1
        if _2r:  # 如果可以计算出2r的值 那么就可以使用正弦定理
            for s in rest_sides:
                f2 = f2.subs(s, _2r * sin(str(s).upper()))
        if f2 != f1:
            self.steps.append(['利用正弦定理得:', ''])
            self.steps.append([BaseEq([f_symbol, f2]).printing(), ''])
        # --------------------------处理角--------------------
        rest_angles = set(f2.free_symbols).intersection({_A, _B, _C})  # 剩下的角
        left = 0  # 区间的左边
        # right = S.Infinity  # 区间的右边
        right = pi  # 区间的右边
        f3 = f2
        if len(angle_values) == 1:  # 如果知道其中一个角 那么其他角的范围就确定了
            right = pi - list(angle_values.values())[0]
        if angle_values and len(angle_values) == 1 and len(rest_angles) == 2:  # 如果知道某一个角的值
            a1, a2 = sorted(rest_angles, key=str)
            a2_sub = pi - list(angle_values.values())[0] - a1
            f3 = f3.subs(a2, a2_sub)
            right = pi - list(angle_values.values())[0]
        if f3 != f2:
            self.steps.append(['  =', new_latex(f3)])
            f3 = su.i_trig_simplify(f3)
            self.steps.append(['  =', new_latex(f3)])
        symb = list(f3.free_symbols)[0]
        self.output.append(BasePoly(f3))
        self.output.append(BaseSymbolValue({symb: Interval.open(left, right)}))
        return self


# 已知三角形中某一个角 求另外两个角关系的函数的取值范围
# style Input: paramer1: 角函数;  paramer2:文本(最大值, 最小值, 范围)
class SanJiaoXingJiaoHanShuFanWei(BaseFunction):
    def solver(self, *args):
        self.label.add('已知角C，求sinA±cosB型范围问题')
        arg1 = args[0]
        src_expr = list(co.flatten([arg1.sympify()]))[-1]  # 未化简前的函数
        src_expr_symbs = list(src_expr.free_symbols)  # 未化简前的变量
        arg2 = args[1]
        text = arg2
        _solver = Triangle2TrigF(known=self.known).solver(arg1)
        for ll in _solver.label:
            self.label.add(ll)
        self.steps.extend(_solver.steps)
        f_expr_simp = _solver.output[0].sympify()
        simp_expr_symb = list(f_expr_simp.free_symbols)[0]  # 化简后的变量 只含有一个
        angle1, angle1_intl = list(_solver.output[1].sympify().items())[0]
        if text.find('范围') >= 0 or text.find('值域') >= 0:
            result = SanJiaoHanShuZuiZhi(known={angle1: angle1_intl}).solver(
                BaseEq(['y', f_expr_simp]), '取值范围').output[0].interval
            self.steps.append(['求得函数的取值范围为:', new_latex(result)])
            self.output.append(co.to_base_interval(result))
        elif text.find('最大值') >= 0:
            result = SanJiaoHanShuZuiZhi(known={angle1: angle1_intl}).solver(BaseEq(['y', f_expr_simp]), '最大值').output[
                0].sympify()
            self.steps.append(['求得函数的最大值为:', new_latex(result)])
            self.output.append(BaseNumber(result))
            expr_symb_value = su.solve_trig_eq(BaseEq([f_expr_simp, result]), symbol_interval=angle1_intl)[0]
            # self.output.append(BaseSymbolValue({simp_expr_symb: expr_symb_value}))
            if len(src_expr_symbs) == 2:  # 如果原来的表达式有两个变量
                other_symb = list(set(src_expr_symbs) - {simp_expr_symb})[0]
                _f = src_expr.subs(simp_expr_symb, expr_symb_value)
                _intl = Interval.open(0, pi - expr_symb_value)
                other_symb_value = su.solve_trig_eq(BaseEq([_f, result]), symbol_interval=_intl)[0]
                self.output.append(BaseSymbolValue({simp_expr_symb: expr_symb_value, other_symb: other_symb_value}))
            else:
                self.output.append(BaseSymbolValue({simp_expr_symb: expr_symb_value}))
        elif text.find('最小值') >= 0:
            result = SanJiaoHanShuZuiZhi(known={angle1: angle1_intl}).solver(BaseEq(['y', f_expr_simp]), '最小值').output[
                0].sympify()
            self.steps.append(['求得函数的最小值为:', new_latex(result)])
            self.output.append(BaseNumber(result))
            expr_symb_value = su.solve_trig_eq(BaseEq([f_expr_simp, result]), symbol_interval=angle1_intl)[0]
            # self.output.append(BaseSymbolValue({simp_expr_symb: expr_symb_value}))
            if len(src_expr_symbs) == 2:  # 如果原来的表达式有两个变量
                other_symb = list(set(src_expr_symbs) - {simp_expr_symb})[0]
                _f = src_expr.subs(simp_expr_symb, expr_symb_value)
                _intl = Interval.open(0, pi - expr_symb_value)
                other_symb_value = su.solve_trig_eq(BaseEq([_f, result]), symbol_interval=_intl)[0]
                self.output.append(BaseSymbolValue({simp_expr_symb: expr_symb_value, other_symb: other_symb_value}))
            else:
                self.output.append(BaseSymbolValue({simp_expr_symb: expr_symb_value}))
        else:
            raise Exception('Type Match Error')
        return self


if __name__ == '__main__':
    pass
